package model.map;

import java.util.*;

/**
 * This class represents the game map as a savable object.
 * @author Zachary
 */
public class SaveMap {
	private int xDimension;
	private int yDimension;
	
	private Map<MapPoint, List<String>> entities;
	private Map<MapPoint, List<String>> mapAoEs;
	private Map<MapPoint, List<String>> traps;
	private Map<MapPoint, List<String>> items;
	private Map<MapPoint, List<String>> gold;
	private Map<MapPoint, Queue<String>> states;
	private Map<MapPoint, String> stateName;
	
	/**
	 * Constructor for the SaveMap.
	 * MUST BE THE SAME SIZE AS THE GAMEMAP!!!!
	 * @param xDim
	 * @param yDim
	 */
	public SaveMap(int xDim, int yDim){
		this.xDimension = xDim;
		this.yDimension = yDim;
		
		this.mapAoEs = new HashMap<MapPoint, List<String>>();
		this.entities = new HashMap<MapPoint, List<String>>();
		this.traps = new HashMap<MapPoint, List<String>>();
		this.items = new HashMap<MapPoint, List<String>>();
		this.gold = new HashMap<MapPoint, List<String>>();
		this.states = new HashMap<MapPoint, Queue<String>>();
		this.stateName = new HashMap<MapPoint, String>();
	}
	
	/**
	 * Returns whether or not a point is valid.
	 * @param point
	 * @return
	 */
	public boolean validLocation(MapPoint point){
		int xCoord = point.getXCoordinate();
		int yCoord = point.getYCoordinate();
		return xCoord <= this.xDimension && yCoord <= this.yDimension &&
			xCoord >= 0 && yCoord >= 0;
	}
	
	/**
	 * Save the mapAoEs on a map point.
	 * @param point
	 * @param name
	 */
	public void saveMapAoE(MapPoint point, String name){
		List<String> list = this.mapAoEs.get(point);
		if(list == null){
			list = new ArrayList<String>();
			mapAoEs.put(point, list);
		}
		if(validLocation(point))
			list.add(name);
	}
	
	/**
	 * Save the entities on a map point.
	 * @param point
	 * @param name
	 */
	public void saveEntity(MapPoint point, String name){
		List<String> list = this.entities.get(point);
		if(list == null){
			list = new ArrayList<String>();
			entities.put(point, list);
		}
		if(validLocation(point))
			list.add(name);
	}
	
	/**
	 * Save the traps on a map point.
	 * @param point
	 * @param name
	 */
	public void saveTrap(MapPoint point, String name){
		List<String> list = this.traps.get(point);
		if(list == null){
			list = new ArrayList<String>();
			traps.put(point, list);
		}
		if(validLocation(point))
			list.add(name);
	}
	
	/**
	 * Save the items on a map point.
	 * @param point
	 * @param name
	 */
	public void saveItem(MapPoint point, String name){
		List<String> list = this.items.get(point);
		if(list == null){
			list = new ArrayList<String>();
			items.put(point, list);
		}
		if(validLocation(point))
			list.add(name);
	}
	
	/**
	 * Save the gold on a map point.
	 * @param point
	 * @param name
	 */
	public void saveGold(MapPoint point, String name){
		List<String> list = this.gold.get(point);
		if(list == null){
			list = new ArrayList<String>();
			gold.put(point, list);
		}
		if(validLocation(point))
			list.add(name);
	}
	
	/**
	 * Save the state of a map point.
	 * @param point
	 * @param name
	 */
	public void saveState(MapPoint point, String name, Queue<String> renderables){
		if(validLocation(point)){
			this.stateName.put(point, name);
			this.states.put(point, renderables);
		}
	}
	
	/**
	 * Return a string representation of all mapAoEs.
	 * @return
	 */
	private String writeMapAoEs(){
		StringBuilder ret = new StringBuilder();
		ret.append("[MapAreaOfEffects]\n");
		
		Set<MapPoint> points = mapAoEs.keySet();
		for(MapPoint p : points){
			List<String> e = mapAoEs.get(p);
			ret.append(p.toString());
			ret.append(" ");
			for(String s : e){
				ret.append(s);
				ret.append("~");
			}
			ret.append(";");
		}
		ret.append("\n");
		
		return ret.toString();
	}
	
	/**
	 * Return a string representation of the traps.
	 * @return
	 */
	private String writeTraps(){
		StringBuilder ret = new StringBuilder();
		ret.append("[Traps]\n");
		
		Set<MapPoint> points = traps.keySet();
		for(MapPoint p : points){
			List<String> e = traps.get(p);
			ret.append(p.toString());
			ret.append(" ");
			for(String s : e){
				ret.append(s);
				ret.append("~");
			}
			ret.append(";");
		}
		ret.append("\n");
		
		return ret.toString();
	}

	/**
	 * Return a string representation of the entities.
	 * @return
	 */
	private String writeEntities(){
		StringBuilder ret = new StringBuilder();
		ret.append("[Entities]\n");
		
		Set<MapPoint> points = entities.keySet();
		for(MapPoint p : points){
			List<String> e = entities.get(p);
			ret.append(p.toString());
			ret.append(" ");
			for(String s : e){
				ret.append(s);
				ret.append("~");
			}
			ret.append(";");
		}
		ret.append("\n");
		
		return ret.toString();
	}
	
	/**
	 * Return a string representation of the items.
	 * @return
	 */
	private String writeItems(){
		StringBuilder ret = new StringBuilder();
		ret.append("[Items]\n");
		
		Set<MapPoint> points = items.keySet();
		for(MapPoint p : points){
			List<String> e = items.get(p);
			ret.append(p.toString());
			ret.append(" ");
			for(String s : e){
				ret.append(s);
				ret.append("~");
			}
			ret.append(";");
		}
		ret.append("\n");
		
		return ret.toString();
	}
	
	/**
	 * Return a string representation of the gold.
	 * @return
	 */
	private String writeGold(){
		StringBuilder ret = new StringBuilder();
		ret.append("[Gold]\n");
		
		Set<MapPoint> points = gold.keySet();
		for(MapPoint p : points){
			List<String> e = gold.get(p);
			ret.append(p.toString());
			ret.append(" ");
			for(String s : e){
				ret.append(s);
				ret.append("~");
			}
			ret.append(";");
		}
		ret.append("\n");
		
		return ret.toString();
	}
	
	/**
	 * Return a string representation of the states.
	 * @return
	 */
	private String writeStates(){
		StringBuilder ret = new StringBuilder();
		ret.append("[States]\n");
		
		Set<MapPoint> points = states.keySet();
		for(MapPoint p : points){
			ret.append(p.toString());
			ret.append(" ");
			ret.append(stateName.get(p));
			ret.append(" ");
			
			Queue<String> renderables = this.states.get(p);
			for(String s : renderables)
				ret.append(s + "~");
			ret.append(";");
		}
		return ret.toString();
	}
	
	/**
	 * Return the overall saved string.
	 * @return
	 */
	public String saveFile(){
		StringBuilder toWrite = new StringBuilder();
		toWrite.append("GameMap.save\n");
		
		toWrite.append(writeItems());
		toWrite.append(writeMapAoEs());
		toWrite.append(writeEntities());
		toWrite.append(writeTraps());
		toWrite.append(writeGold());
		toWrite.append(writeStates());
		
		return toWrite.toString();
	}
}
